﻿using System.Security.Cryptography;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.OpenSsl;

namespace Aurora.Security.RSA.Facility
{
    internal class RSAPkcs1Facility : RSAFacilityBase
    {
        public RSAPkcs1Facility(string publicKey, string privateKey = null, int keySize = 2048)
        {
            if (string.IsNullOrEmpty(privateKey) && string.IsNullOrEmpty(publicKey))
            {
                throw new Exception("公钥和私钥不能同时为空");
            }

            if (!string.IsNullOrEmpty(privateKey))
            {
                _privateRsa = System.Security.Cryptography.RSA.Create();
                _privateRsa.KeySize = keySize;
                _privateRsa.ImportParameters(GetPrivateKeyInfo(privateKey));
            }

            if (!string.IsNullOrEmpty(publicKey))
            {
                _publicRsa = System.Security.Cryptography.RSA.Create();
                _publicRsa.KeySize = keySize;
                _publicRsa.ImportParameters(GetPublicKeyInfo(publicKey));
            }
        }

        public RSAPkcs1Facility(RSAParameters parameters, int keySize = 2048)
        {
            var rsa = System.Security.Cryptography.RSA.Create();
            rsa.KeySize = keySize;
            rsa.ImportParameters(parameters);
            _privateRsa = rsa;
            _publicRsa = rsa;
        }

        /// <summary>
        /// 获取公钥信息
        /// </summary>
        /// <param name="publicKey"></param>
        /// <returns></returns>
        private RSAParameters GetPublicKeyInfo(string publicKey)
        {
            using (var txtReader = new StringReader(PublicKeyFormat(publicKey)))
            {
                AsymmetricKeyParameter keyParameter = (AsymmetricKeyParameter)new PemReader(txtReader).ReadObject();
                var parameters = new RSAParameters();
                if (keyParameter == null)
                    throw new ArgumentNullException("key");

                if (keyParameter is RsaKeyParameters _key)
                {
                    parameters.Modulus = _key.Modulus.ToByteArrayUnsigned();
                    parameters.Exponent = _key.Exponent.ToByteArrayUnsigned();
                }
                return parameters;
            }
        }

        /// <summary>
        /// 格式化公钥
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        private string PublicKeyFormat(string str)
        {
            if (str.StartsWith("-----BEGIN PUBLIC KEY-----"))
            {
                return str;
            }
            List<string> res = new List<string>();
            res.Add("-----BEGIN PUBLIC KEY-----");
            int pos = 0;

            while (pos < str.Length)
            {
                var count = str.Length - pos < 64 ? str.Length - pos : 64;
                res.Add(str.Substring(pos, count));
                pos += count;
            }
            res.Add("-----END PUBLIC KEY-----");
            var resStr = string.Join("\r\n", res);
            return resStr;
        }

        /// <summary>
        /// 获取私钥信息
        /// </summary>
        /// <param name="privateKey"></param>
        /// <returns></returns>
        private RSAParameters GetPrivateKeyInfo(string privateKey)
        {
            using (var txtReader = new StringReader(Pkcs1PrivateKeyFormat(privateKey)))
            {
                var keyPair = (AsymmetricCipherKeyPair)new PemReader(txtReader).ReadObject();
                var key = keyPair.Private;
                var parameters = new RSAParameters();
                if (key == null)
                    throw new ArgumentNullException("key");
                if (!key.IsPrivate)
                    throw new ArgumentException("Public key passed - private key expected", "key");

                if (key is RsaKeyParameters)
                {
                    if (key is RsaPrivateCrtKeyParameters)
                    {
                        RsaPrivateCrtKeyParameters _key = (RsaPrivateCrtKeyParameters)key;
                        parameters.Modulus = _key.Modulus.ToByteArrayUnsigned();
                        parameters.D = _key.Exponent.ToByteArrayUnsigned();
                        parameters.Exponent = _key.PublicExponent.ToByteArrayUnsigned();
                        parameters.P = _key.P.ToByteArrayUnsigned();
                        parameters.Q = _key.Q.ToByteArrayUnsigned();
                        parameters.DP = _key.DP.ToByteArrayUnsigned();
                        parameters.DQ = _key.DQ.ToByteArrayUnsigned();
                        parameters.InverseQ = _key.QInv.ToByteArrayUnsigned();
                    }
                    else
                    {
                        RsaKeyParameters _key = (RsaKeyParameters)key;
                        parameters.Modulus = _key.Modulus.ToByteArrayUnsigned();
                        parameters.D = _key.Exponent.ToByteArrayUnsigned();
                        parameters.Exponent = BigInteger.Zero.ToByteArrayUnsigned();
                        parameters.P = BigInteger.Zero.ToByteArrayUnsigned();
                        parameters.Q = BigInteger.Zero.ToByteArrayUnsigned();
                        parameters.DP = BigInteger.Zero.ToByteArrayUnsigned();
                        parameters.DQ = BigInteger.Zero.ToByteArrayUnsigned();
                        parameters.InverseQ = BigInteger.Zero.ToByteArrayUnsigned();
                    }
                }
                return parameters;
            }
        }

        /// <summary>
        /// 格式化私钥
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        private string Pkcs1PrivateKeyFormat(string str)
        {
            if (str.StartsWith("-----BEGIN RSA PRIVATE KEY-----"))
            {
                return str;
            }

            List<string> res = new List<string>();
            res.Add("-----BEGIN RSA PRIVATE KEY-----");

            int pos = 0;

            while (pos < str.Length)
            {
                var count = str.Length - pos < 64 ? str.Length - pos : 64;
                res.Add(str.Substring(pos, count));
                pos += count;
            }

            res.Add("-----END RSA PRIVATE KEY-----");
            var resStr = string.Join("\r\n", res);
            return resStr;
        }
    }
}